﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Threading;
using System.Windows.Input;
using ApiScanner.Core;
using ApiScannerShell.Views;
using InfiniTec.Threading;

namespace ApiScannerShell.ViewModels
{
    public sealed class TestRunnerViewModel: ViewModel<TestRunnerViewModel, TestRunner>
    {
        private readonly SynchronizationContext _SynchronizationContext = SynchronizationContext.Current ??
                                                                 new SynchronizationContext();

        private readonly ObservableCollection<Artifact> _Errors = new ObservableCollection<Artifact>();
        private readonly ObservableCollection<Artifact> _Events = new ObservableCollection<Artifact>();
        private readonly ObservableCollection<MessageEntry> _Messages = new ObservableCollection<MessageEntry>();

        public IEnumerable<Artifact> Errors { get { return _Errors; } }
        public IEnumerable<Artifact> Events { get { return _Events; } }
        public IEnumerable<MessageEntry> Messages { get { return _Messages; } }

        public ICommand CloseCommand { get; private set; }

        public MessageEntry CurrentAction
        {
            get { return _Messages.Count == 0 ? null : _Messages[_Messages.Count - 1]; }
        }
        public bool IsRunning
        {
            get { return GetValue(v => v.IsRunning); }
            set { SetValue(v => v.IsRunning, value); }
        }
        
        public DateTime StartTime
        {
            get { return GetValue(v => v.StartTime); }
            private set { SetValue(v => v.StartTime, value);}
        }

        public TimeSpan ElapsedTime
        {
            get { return TimeSpan.FromSeconds((int) (DateTime.Now - StartTime).TotalSeconds); }
        }

        private void UpdateElapsedTime()
        {
            Dispatcher.RegisterCallback(TimeSpan.FromSeconds(1),
                                        state =>
                                            {
                                                SafeUpdateUI(() => OnPropertyChanged(v=>v.ElapsedTime));
                                                if (IsRunning) UpdateElapsedTime();
                                            }, null);
        }

        public TestRunnerViewModel(TestRunner testRunner): base(testRunner)
        {
            CloseCommand = new DelegateCommand<TestRunnerView>(view => !IsRunning,
                                                                       view => view.DialogResult = true);
            UpdateElapsedTime();
            IsRunning = true;
            StartTime = DateTime.Now;
            StartAsync();
        }

        private void StartAsync()
        {
            ThreadPool.QueueUserWorkItem(state => Start());
        }

        private void SafeUpdateUI(Action action)
        {
            _SynchronizationContext.Send(state => action(), null);
        }

        private void Start()
        {
            foreach (var artifact in Model.Run(ProgressHandler))
            {
                var error = artifact as ScannerError;
                if (error != null)
                {
                    SafeUpdateUI(() => _Errors.Add(error));
                }
                else
                {
                    var eventBinding = artifact;
                    SafeUpdateUI(() => _Events.Add(eventBinding));
                }
            }
            SafeUpdateUI(() =>
                             {
                                 IsRunning = false;
                                 CommandManager.InvalidateRequerySuggested();
                             });
        }

        private void ProgressHandler(string s)
        {
            SafeUpdateUI(() =>
                             {
                                 _Messages.Add(new MessageEntry(s));
                                 OnPropertyChanged(v => v.CurrentAction);
                             });
        }
    }
}